//
//  ViewController.swift
//  Left Navigation bar
//
//  Created by Joel on 2020/7/3.
//  Copyright © 2020 Joel. All rights reserved.
//

import Cocoa

// MARK:- Group and Item definitions

// 子标签类
class GroupItem {
    var name: String // 名字
    var icon: String // 图标
    
    init(name: String, icon: String) {
        self.name = name
        self.icon = icon
    }
}

class Group {
    // 检查一个对象是否是该类的有效实例
    static func isInstance(any: Any)-> Bool {
        if let _ = any as? Group {
            return true
        }
        return false
    }
    
    var name: String // 组名
    var items: [GroupItem] // 子标签
    
    // 获取当前组有多少个子标签
    var numberOfItems: Int {
        get {
            return self.items.count
        }
    }
    
    init(name: String) {
        self.name = name
        self.items = []
    }
    
    // 为当前组添加一个子标签
    func addChild(item: GroupItem) {
        self.items.append(item)
    }
    
    // 根据下标获取一个子标签
    // 通常来说下标一定不会越界，但是为了安全我们在下表越界之后返回一个无效的标签对象
    func childAt(index: Int) -> GroupItem {
        if index >= 0 && index < numberOfItems {
            return self.items[index]
        } else {
            return GroupItem(name: "ERROR", icon: NSImage.stopProgressTemplateName)
        }
    }
}

// MARK:- View controller
class ViewController: NSViewController {
    @IBOutlet var outline: NSOutlineView!
    
    var data: [Group]!
    override func viewDidLoad() {
        super.viewDidLoad()
        initData()
        // 将 floatsGroupRows 设置为false, 可以禁止第一行点击展开时往上浮动的现象
        outline.floatsGroupRows = false
        outline.dataSource = self
        outline.delegate = self
    }
    
    override func viewDidAppear() {
        // 在界面即将显示的时候展开所有标签组
        outline.expandItem(nil, expandChildren: true)
    }

    override var representedObject: Any? {
        didSet {
            // Update the view, if already loaded.
        }
    }
    
    func initData() {
        // Favorites
        let favorites = Group(name: "Favorites")
        favorites.addChild(item: GroupItem(name: "Google", icon: NSImage.bookmarksTemplateName))
        favorites.addChild(item: GroupItem(name: "百度", icon: NSImage.quickLookTemplateName))
        favorites.addChild(item: GroupItem(name: "CSDN", icon: NSImage.refreshTemplateName))
        favorites.addChild(item: GroupItem(name: "Segmentfault", icon: NSImage.slideshowTemplateName))
        favorites.addChild(item: GroupItem(name: "Joel", icon: NSImage.listViewTemplateName))
        favorites.addChild(item: GroupItem(name: "C++", icon: NSImage.goForwardTemplateName))
        let users = Group(name: "名字")
        users.addChild(item: GroupItem(name: "谷歌", icon: NSImage.lockLockedTemplateName))
        users.addChild(item: GroupItem(name: "Baidu", icon: NSImage.lockUnlockedTemplateName))
        users.addChild(item: GroupItem(name: "Apple", icon: NSImage.enterFullScreenTemplateName))
        users.addChild(item: GroupItem(name: "小猫咪", icon: NSImage.exitFullScreenTemplateName))
        // Transfer list
        let tasks = Group(name: "Tags")
        tasks.addChild(item: GroupItem(name: "Available", icon: NSImage.statusAvailableName))
        tasks.addChild(item: GroupItem(name: "None", icon: NSImage.statusNoneName))
        tasks.addChild(item: GroupItem(name: "Partially Available", icon: NSImage.statusPartiallyAvailableName))
        tasks.addChild(item: GroupItem(name: "Unavailable", icon: NSImage.statusUnavailableName))
        //
        let empty = Group(name: "Empty")
        //
        self.data = [favorites, empty, users, tasks]
    }
}

// MARK:- View controller with NSOutlineViewDelegate
extension ViewController: NSOutlineViewDelegate {
    // 判断当前元素是否是一个组元素。
    // 如果返回false， 那么展开按钮是一个三角形并且在文字前面。
    // 如果返回true，那么展开按钮是一个悬浮才可见的纯文字按钮（仅有文字没有边框）并且在组标签的后面（靠近outline view 的右边界）
    func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool {
        Group.isInstance(any: item)
    }
    
    // 判断当前元素是否可以被选中。
    // 默认情况下是允许任意节点被选中的，我们希望表示分组的节点不可以被选中，只有子标签可以被选中。
    func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool {
        !Group.isInstance(any: item)
    }
    
    // 获取每一行的视图
    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
        var ret: NSView?
        if let group = item as? Group {
            let cell = outlineView.makeView(withIdentifier: .init("HeaderCell"), owner: self) as! NSTableCellView
            // 注意：此处我们不能改变 cell.textField，调用 cell.textField = myTextField是无效的。
            // 只能修改它的内容。
            cell.textField!.stringValue = group.name
            ret = cell
        } else if let groupItem = item as? GroupItem {
            let cell = outlineView.makeView(withIdentifier: .init("DataCell"), owner: self) as! NSTableCellView
            // 注意：此处我们不能改变 cell.textField，只能修改它的内容。
            cell.textField!.stringValue = groupItem.name
            // 同上，我们只能修改 imageView 里面的图片，调用 cell.imageView = myImageView是无效的
            cell.imageView?.image = NSImage(named: groupItem.icon)
            ret = cell
        }
        return ret
    }
}

// MARK:- View controller with NSOutlineViewDataSource
extension ViewController: NSOutlineViewDataSource {
    // 询问当前组有多少个子节点。
    // 值得注意的是 Outline 初始化时会调用这个方法，但是 item 是nill. 所以第一次调用的意思是询问有多少个分组。
    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
        if let group = item as? Group {
            return group.numberOfItems
        }
        return self.data.count
    }
    
    // 根据下标获取某个分组下面的子节点数据
    func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
        if let group = item as? Group {
            return group.childAt(index: index)
        }
        return self.data[index]
    }
    
    // 询问当前节点是否可以被展开，会在某个标签进入可见状态时调用。
    // 如果返回 false 表示此行当前不可被展开，即不显示展开按钮。
    // 大多数情况下，即使当前组下面没有任何子标签我们也让他显示展开按钮
    func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
       Group.isInstance(any: item)
    }
}
